package com.chang.product.web;
/*
 * @Author Changzhenxi
 * @Date 2024/6/12 20:53
 * @Description This is description of method
 */

import com.chang.product.entity.CategoryEntity;
import com.chang.product.service.CategoryService;
import com.chang.product.utils.redis.RedisUtils;
import com.chang.product.vo.CatelogTwoVo;
import jakarta.annotation.Resource;
import org.redisson.Redisson;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {

    @Resource
    CategoryService categoryService;

    @Resource
    Redisson redisson;

    @GetMapping({"/", "index.html"})
    public String indexPage(Model model) {

        // 将实际的产品数据查出来进行拼接到前端页面
        List<CategoryEntity> categoryList = categoryService.getLevelOneCategorys();
        model.addAttribute("categorys", categoryList);
        return "index"; // mvc视图解析器自动拼接后缀 匹配首页地址
    }

    // 点击一级菜单显示下一级别内容
    @ResponseBody //返回json数据，需要添加注解@ResponseBody
    @GetMapping("index/catelogJson")
    public Map<String, List<CatelogTwoVo>> getCatelogJsonFromRedis() throws InterruptedException {
        return categoryService.getCatelogJsonFromRedis();
    }

    /*
     * @Date 2024/8/12 16:37
     * @Author changzhenxi
     * @Description
     **/
    @GetMapping("hello")
    @ResponseBody
    public String hello() {
        // 获取锁，只要锁的名字一样，就是同一把锁
        RLock lock = redisson.getLock("AAAALock");
        try {
            // lock.lock(); // 阻塞式等待，默认加锁时间都是30秒
            // 只要占锁成功，就会启动一个定时任务(重新给锁设置过期时间,新的过期时间就是看门狗的默认时间) 三分之一的看门狗时间(10秒)之后自动续机 续机到30秒
            // 如果获取锁后业务超长，则运行期间看门狗会自动给锁续机加钟，不用担心业务时间过长导致锁自动过期被删除
            // 加锁的业务只要运行完成，就不会给当前锁续机加钟，及时不手动解锁，锁默认也会在30秒之后自动删除  看门狗默认30秒
            lock.lock(1, TimeUnit.SECONDS); // 如果设置了指定的时间,不管业务有没有执行完就都不会自动续机延长存在锁的时间
            System.out.println("加锁成功~执行业务" + Thread.currentThread().getId());
            Thread.sleep(10000);
            // 最佳时间就是直接显式指定过期时间,省掉了续机操作,然后手动解锁
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 解锁
            lock.unlock();
            System.out.println("解锁成功" + Thread.currentThread().getId());
        }
        return "hello";
    }

    // 读写锁可以保证一定能读到最新数据 写锁式一个排他锁（互斥锁） 读锁是一个共享锁
    @GetMapping("/write")
    @ResponseBody
    public String write() {
        String random;
        RLock readWriteLock = redisson.getReadWriteLock("readWriteLock").writeLock();
        try {
            readWriteLock.lock();
            System.out.println("写锁加锁成功" + Thread.currentThread().getId());
            random = UUID.randomUUID().toString();
            Thread.sleep(10000);
            RedisUtils.set("writeValue", random);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            readWriteLock.unlock();
            System.out.println("写锁释放完毕~" + Thread.currentThread().getId());
        }
        return RedisUtils.get("writeValue");
    }

    /*
     * @Date 2024/8/13 15:02
     * @Author changzhenxi
     * @Description 读锁测试 注意读读之间如果用同一个浏览器访问会自动变为串行,影响观测,可以一个使用浏览器一个使用postman即可验证读读不互斥
     **/
    @GetMapping("/read")
    @ResponseBody
    public String read() {
        RLock readWriteLock = redisson.getReadWriteLock("readWriteLock").readLock();
        String currentNum;
        readWriteLock.lock();
        try {
            System.out.println("读锁加锁完毕" + Thread.currentThread().getId());
            Thread.sleep(10000);
            currentNum = RedisUtils.get("writeValue");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("读锁释放完毕~" + Thread.currentThread().getId());
            readWriteLock.unlock();
        }
        return currentNum;
    }


    /*
     * @Date 2024/8/13 15:35
     * @Author changzhenxi
     * @Description getSemaphore分布式信号量测试
     * 这里的使用场景比如 简单实现令牌桶算法的请求限流操作,
     **/
    @GetMapping("stop")
    @ResponseBody
    public String acquire() {
        RSemaphore light = redisson.getSemaphore("light");
        //light.acquire(); // 获取一个信号量 占用一个位置
        if (light.tryAcquire()) { // 尝试获取信号量
            System.out.println("尝试占用信号量成功,当前剩余信号量:" + RedisUtils.get("light"));
        } else {
            System.out.println("尝试占用信号量失败,当前剩余信号量:" + RedisUtils.get("light"));
        }
        return "ok";
    }

    @GetMapping("go")
    @ResponseBody
    public String release() {
        RSemaphore light = redisson.getSemaphore("light");
        light.release(); // 释放一个信号量
        System.out.println("信号量释放完毕,当前剩余信号量:" + RedisUtils.get("light"));
        return "ok";
    }

    /*
     * @Date 2024/8/13 16:18
     * @Author changzhenxi
     * @Description  闭锁操作 等到所有闭锁操作[countDown()]完成之后 才会继续执行 就像下面两个请求,
     **/
    @GetMapping("goPark")
    @ResponseBody
    public String goPark() throws InterruptedException {
        RCountDownLatch goPark = redisson.getCountDownLatch("goPark");
        goPark.trySetCount(5L); // 设置初始闭锁数量为5
        goPark.await(); // 等待所有闭锁完成
        return "车上座位已经坐满了  出发去公园!";
    }

    @GetMapping("getCar")
    @ResponseBody
    public String getCar() {
        RCountDownLatch goPark = redisson.getCountDownLatch("goPark");
        goPark.countDown(); //闭锁每完成一次,初始计数减一
        return "已经上车了~ gogogo";
    }



}
